/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.utils;

import java.io.*;
import java.text.*;
import java.util.*;

/**
 * This class is a logging class that writes it's log out to a file.
 * The home directory of all the files is given when the class is created.
 * Per hour a new file is created the nameing convention is as follows.
 * <CODE>
 * Log_YYYY_MM_DD_HH.log
 * </CODE>
 * This way the log file will be easy to sort by date in any file system as well
 * as small. For hours when no loggin occures there will be no log file.
 */
public class LoggingFile extends Logging {
	SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd 'at' HH'h'mm.ss");
	static SimpleDateFormat filename_format = new SimpleDateFormat("'Log'yyyy-MM-dd_HH'.log'");
	String lastfilename = null;
	String location;
	String stop_date;
	OutputStreamWriter output = null;
	static String newname = null;
	boolean inexception = false;
	boolean dont_log = false;

	public LoggingFile(String p_location, boolean debug, boolean dev,boolean trace, boolean critical)
	throws IOException {
		super(debug, dev, trace, critical,false);
		location = p_location;
		if (!p_location.endsWith("\\") && !p_location.endsWith("/")) {
			p_location = p_location + '/';
		}
		Log.trace.out( "LoggingFile", "Logging started in '" + p_location + "'");
	}

	public LoggingFile(String p_location, boolean debug, boolean trace, boolean critical)
	throws IOException {
		this(p_location,debug, false, trace, critical);		
	}
	
	public static File getLastLogFile() {
		if (newname != null) {
			return new File(newname);
		}
		return null;
	}
	
	public void write(String level, long thread_id, String description, String details) {
		try {
			if (dont_log) return ;
			buildOutputFile();
			output.write(
				level + " [" + thread_id + "] " + date_format.format(new Date()) + "\n" +
				"Description: " + description + "\n" +
				details + "\n" +
				"-------------------------------------\n"
			);
			output.flush();
			output.close();
			setExceptionModeOff();
		} catch (IOException ioe) {
			setExceptionModeOn(ioe);
		}
	}
	/*
		public void writeDebug(String description, String details) {
			try {
				if (dont_log) return;
				buildOutputFile();
				output.write(
					"DBG " + date_format.format(new Date()) + "\n" +
					"Description: " + description + "\n" +
					details + "\n" +
					"-------------------------------------\n"
				);
				output.flush();
				output.close();
				setExceptionModeOff();
			} catch (IOException ioe) {
				setExceptionModeOn(ioe);
			}
		}

		public void writeCritical(String description, String details) {
			try {
				if (dont_log) return;
				buildOutputFile();
				output.write(
					"CRT *** " + date_format.format(new Date()) + "\n" +
					"Description: " + description + "\n" +
					details + "\n" +
					"-------------------------------------\n"
				);
				output.flush();
				output.close();
				setExceptionModeOff();
			} catch (IOException ioe) {
				setExceptionModeOn(ioe);
			}
		}
	*/
	/**
	 * sets the exception mode off
	 */
	private void setExceptionModeOff() {
		// Was there previosly an exception
		if (inexception) {
			// exception mode to false
			inexception = false;
			// Log details
			//Log.writeLog(CRITICAL, "Logging Error Solved", "Loggin message to file were lost between " + stop_date + " and " + new Date() + ". Please investigate this issue");
		}
	}
	/**
	 * sets the exception mode for logging.
	 */
	private void setExceptionModeOn(IOException ioe) {
		// Only log this once
		if (!inexception) {
			// Remeber the date time of the stop
			stop_date = date_format.format(new Date());
			// Not ionm exception to prevent repeated view of ths message
			inexception = true;
			// Write critical error to screen output
			dont_log = true;
			Log.critical.out(ioe);
			dont_log = false;
			// Set name to null so will try again and again until sucess is achived
			lastfilename = null;
		}
	}

	/**
	 * Build the new file writer for outputing the logs to.
	 */
	private void buildOutputFile()
	throws IOException {
		File directory = new File(location);
		if (!directory.exists()) {
			if (!directory.mkdirs()) {
				throw new IOException("Can't build [" + directory + "]");
			}
		}
		// Work out name that would be used now
		newname = location + filename_format.format(new Date());
		// If file does exist then use that file, else create new one
		try {
			output = new FileWriter(newname, true);
		} catch (FileNotFoundException fnfe) {
			output = new FileWriter(newname);
		}
	}

	public void close() {
		try {
			if (output != null) {
				output.close();
			}
		} catch (IOException ioe) {}
	}

	private LogDataUnit loadLogDataUnitFromString(String buffer, Integer tid, Date from_date, Date to_date, Logging.Level levels[])
	throws IOException {
		if (buffer.indexOf("-------------------------------------") != -1) {
			String data_unit = buffer.substring(0, buffer.indexOf("-------------------------------------") + "-------------------------------------".length());
			buffer = buffer.substring(buffer.indexOf("-------------------------------------") + "-------------------------------------".length());
			String level_str = data_unit.substring(0, data_unit.indexOf("[") - 1);
			String tid_str = data_unit.substring(data_unit.indexOf("[") + 1, data_unit.indexOf("]"));
			data_unit = data_unit.substring(data_unit.indexOf("]") + 2);
			String date_date_time = data_unit.substring(0, data_unit.indexOf("\n"));
			data_unit = data_unit.substring(data_unit.indexOf("\n") + 1);
			String description = data_unit.substring(data_unit.indexOf(":") + 1, data_unit.indexOf("\n"));
			data_unit = data_unit.substring(data_unit.indexOf("\n") + 1);
			String detail = data_unit.substring(0, data_unit.indexOf("-------------------------------------"));
			Logging.Level level = null;
			if (tid != null && Integer.parseInt(tid_str) != tid.intValue()) {
				return null;
			}
			if (level_str.equals(Logging.TRACE)) {
				level = Logging.TRACE;
			}
			if (level_str.equals(Logging.DEBUG)) {
				level = Logging.DEBUG;
			}
			if (level_str.equals(Logging.CRITICAL)) {
				level = Logging.CRITICAL;
			}
			// Make sure that level is in chosen levels
			boolean found_level = false;
			for (int i = 0;i < levels.length && found_level == false;i++) {
				if (levels[i].toString().equals(level.toString())) {
					found_level = true;
				}
			}
			if (!found_level) {
				return null;
			}
			// Make sure that the date is in range
			Date current_date = null;
			try {
				current_date = date_format.parse(date_date_time);
				if (!(current_date.getTime() >= from_date.getTime() && current_date.getTime() <= to_date.getTime())) {
					return null;
				}
			} catch (java.text.ParseException pe) {
				throw new IOException(pe.getMessage());
			}
			return new LogDataUnit(level, description, detail,current_date);
		}
		return null;
	}

	/**
	 * Loads all the logs out of the stream
	 */
	private void loadLogDataUnitFromFile(InputStream stream_in, Integer tid, Date from_date, Date to_date, Logging.Level levels[], LogDataUnitVector logs)
	throws IOException {
		byte buffer[] = new byte[130];
		String str_buffer = "";
		while (stream_in.available() > 0) {
			int r = stream_in.read(buffer);
			str_buffer += new String(buffer, 0, r);
			LogDataUnit log_data_unit = loadLogDataUnitFromString(str_buffer, tid, from_date, to_date, levels);
			if (log_data_unit != null) {
				logs.add(log_data_unit);
			}
		}
		// Make sure that you read the last one
		LogDataUnit log_data_unit = loadLogDataUnitFromString(str_buffer, tid, from_date, to_date, levels);
		if (log_data_unit != null) {
			logs.add(log_data_unit);
		}
	}

	/**
	 * retrieve all the logs matching the thread id and from and to_date (which may be null)
	 */
	public LogDataUnitVector findLogs(Integer tid, Date from_date, Date to_date, Logging.Level levels[])
	throws IOException {
		if (from_date == null) {
			from_date = new java.util.Date();
		}
		if (to_date == null) {
			to_date = new java.util.Date();
		}
		// Now iterate through all the dates increase by hours
		Calendar calender = Calendar.getInstance();
		calender.setTime(from_date);
		LogDataUnitVector logs = new LogDataUnitVector();
		for (;calender.getTime().getTime() <= to_date.getTime();calender.add(java.util.Calendar.HOUR, 1)) {
			// locate the file
			if (new File(location + "/" + filename_format.format((calender.getTime()))).exists()) {
				FileInputStream file_in = new FileInputStream(location + "/" + filename_format.format((calender.getTime())));
				loadLogDataUnitFromFile(file_in, tid, from_date, to_date, levels, logs);
			}
		}
		return logs;
	}
}

